package org.gpp.proj1.ui.controller;

import java.io.File;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.logging.Level;

import org.gpp.proj1.communication.CommunicationException;
import org.gpp.proj1.communication.listener.ConnectionAttemptListener;
import org.gpp.proj1.logic.ISRGUIModel;
import org.gpp.proj1.logic.core.board.RobotPosition;
import org.gpp.proj1.logic.core.server.ServerPhaseException;
import org.gpp.proj1.logic.metadata.PartCategory;
import org.gpp.proj1.logic.metadata.PartEntry;
import org.gpp.proj1.logic.metadata.RobotEntry;
import org.gpp.proj1.logic.metadata.RobotPartCategory;
import org.gpp.proj1.logic.profile.PlayerProfile;
import org.gpp.proj1.logic.profile.UnsufficientFundsException;
import org.gpp.proj1.map.GameMap;
import org.gpp.proj1.ui.ISRGUIView;

import com.jme.util.LoggingSystem;

/**
 * @author Paolo Victor, paolovictor@gmail.com
 */
public class SRGUIControllerImpl implements ISRGUIController {
	
	private ISRGUIModel model;
	
	private List<ISRGUIView> views;
	
	private Object uiLock;
	
	public SRGUIControllerImpl( ISRGUIModel model ) {
		this.model = model;
		this.uiLock = new Object();
		this.views = new LinkedList<ISRGUIView>();
	}
	
	/* (non-Javadoc)
	 * @see org.gpp.proj1.ui.controller.ISRGUIController#registerView(org.gpp.proj1.ui.view.ISRGUIView)
	 */
	public void registerView( ISRGUIView view ) {
		this.views.add( view );
	}

	/* (non-Javadoc)
	 * @see org.gpp.proj1.ui.controller.ISRGUIController#createPlayerProfile(java.lang.String)
	 */
	public void createPlayerProfile(String playerName) {
		
		model.createPlayerProfile( playerName );
	}

	/* (non-Javadoc)
	 * @see org.gpp.proj1.ui.controller.ISRGUIController#getPlayerProfileNames()
	 */
	public Set<String> getPlayerProfileNames() {
		
		return model.getPlayerProfileNames();
	}

	/* (non-Javadoc)
	 * @see org.gpp.proj1.ui.controller.ISRGUIController#notifyLoadProfile()
	 */
	public void notifyPromptLoadProfile() {
		for( ISRGUIView view : views ) {
			view.notifyLoadProfile();
		}
	}

	/* (non-Javadoc)
	 * @see org.gpp.proj1.ui.controller.ISRGUIController#notifyProfileNotLoaded()
	 */
	public void notifyProfileNotLoaded() {
		
		this.model.notifyProfileNotLoaded();
	}

	/* (non-Javadoc)
	 * @see org.gpp.proj1.ui.controller.ISRGUIController#notifyAborting(java.lang.String)
	 */
	public void notifyAborting(String message) {
		for( ISRGUIView view : views ) {
			view.notifyAborting(message);
		}
	}

	/* (non-Javadoc)
	 * @see org.gpp.proj1.ui.controller.ISRGUIController#loadProfile(java.lang.String)
	 */
	public void loadProfile(String playerName) {
		
		model.loadProfile( playerName );
	}

	/* (non-Javadoc)
	 * @see org.gpp.proj1.ui.controller.ISRGUIController#notifyLoadedProfile()
	 */
	public void notifyLoadedProfile() {
		
		for( ISRGUIView view : views ) {
			view.notifyLoadedProfile();
		}		
	}

	/* (non-Javadoc)
	 * @see org.gpp.proj1.ui.controller.ISRGUIController#getCurrentProfileName()
	 */
	public PlayerProfile getCurrentProfile() {
		
		return model.getCurrentProfile();
	}

	/* (non-Javadoc)
	 * @see org.gpp.proj1.ui.controller.ISRGUIController#getPartsList()
	 */
	public Map<PartCategory, Map<String, PartEntry>> getPartsList() {
		
		return model.getPartsList();
	}

	/* (non-Javadoc)
	 * @see org.gpp.proj1.ui.controller.ISRGUIController#buyPart(java.lang.String, java.lang.String, int)
	 */
	public void buyPart(PartCategory category, String part, int quantity) throws UnsufficientFundsException {

		model.buyPart( category, part, quantity );
	}

	/* (non-Javadoc)
	 * @see org.gpp.proj1.ui.controller.ISRGUIController#getAvailableParts(org.gpp.proj1.logic.metadata.PartCategory)
	 */
	public Map<PartEntry, Integer> getAvailableParts(PartCategory category) {

		return model.getAvailableParts(category);
	}

	/* (non-Javadoc)
	 * @see org.gpp.proj1.ui.controller.ISRGUIController#saveRobot(java.lang.String, java.lang.String, java.util.Map)
	 */
	public void saveRobot(String oldName, String newName, Map<RobotPartCategory, PartEntry> parts) {

		model.saveRobot( oldName, newName, parts );
	}

	/* (non-Javadoc)
	 * @see org.gpp.proj1.ui.controller.ISRGUIController#getPlayerRobots()
	 */
	public List<RobotEntry> getPlayerRobots() {
		
		return model.getPlayerRobots();
	}

	/* (non-Javadoc)
	 * @see org.gpp.proj1.ui.controller.ISRGUIController#getRobotDescription(java.lang.String)
	 */
	public String getRobotDescription(String robot) {

		return model.getRobotDescription(robot);
	}

	/* (non-Javadoc)
	 * @see org.gpp.proj1.ui.controller.ISRGUIController#deleteRobot(java.lang.String)
	 */
	public void deleteRobot(String robotName) {
		
		model.deleteRobot( robotName );
	}

	/* (non-Javadoc)
	 * @see org.gpp.proj1.ui.controller.ISRGUIController#connectToServer(org.gpp.proj1.communication.listener.ConnectionAttemptListener, java.lang.String, int)
	 */
	public void connectToServer(ConnectionAttemptListener listener, String address, int localPort) throws CommunicationException {

		model.connectToServer( listener, address, localPort );
	}

	/* (non-Javadoc)
	 * @see org.gpp.proj1.ui.controller.ISRGUIController#startServer(java.lang.String, int, java.lang.String, java.io.File, int)
	 */
	public void startServer(String name, int maxPlayers, String mapName, File mapFile, int port) throws CommunicationException {
		
		model.startServer( name, maxPlayers, mapName, mapFile, port );
	}

	/* (non-Javadoc)
	 * @see org.gpp.proj1.ui.controller.ISRGUIController#stopServer()
	 */
	public void stopServer() {
		// TODO Auto-generated method stub
		
	}

	/* (non-Javadoc)
	 * @see org.gpp.proj1.ui.controller.ISRGUIController#isClientConnected()
	 */
	public boolean isClientConnected() {
		return model.isClientConnected();
	}

	/* (non-Javadoc)
	 * @see org.gpp.proj1.ui.controller.ISRGUIController#closeConnection()
	 */
	public void closeConnection() throws CommunicationException {
	
		this.model.closeConnection();
	}

	/* (non-Javadoc)
	 * @see org.gpp.proj1.ui.controller.ISRGUIController#sendChatMessage(java.lang.String)
	 */
	public void sendChatMessage(String text) throws CommunicationException {

		this.model.sendChatMessage( text );
	}

	/* (non-Javadoc)
	 * @see org.gpp.proj1.ui.controller.ISRGUIController#notifyChatMessage(java.lang.String, java.lang.String)
	 */
	public void notifyChatMessage(String alias, String chatMessage) {
		for( ISRGUIView view : views ) {
			view.notifyChatMessage(alias, chatMessage);
		}
	}

	/* (non-Javadoc)
	 * @see org.gpp.proj1.ui.controller.ISRGUIController#notifyWhosHereMessage(java.util.Map, java.util.Set)
	 */
	public void notifyWhosHereMessage(Map<String, String> aliasMap, Set<String> readyClientsSet) {
		
		for( ISRGUIView view : views ) {
			view.notifyWhosHereMessage(aliasMap, readyClientsSet);
		}
	}

	/* (non-Javadoc)
	 * @see org.gpp.proj1.ui.controller.ISRGUIController#notifyChangeMap(java.lang.String)
	 */
	public void notifyChangeMap(String mapName) {
		
		for( ISRGUIView view : views ) {
			view.notifyWhosHereMessage( mapName );
		}
	}

	/* (non-Javadoc)
	 * @see org.gpp.proj1.ui.controller.ISRGUIController#sendChangeMapMessage(java.lang.Object)
	 */
	public void changeMap(String mapName) throws CommunicationException {

		this.model.changeGameMap( mapName );
	}

	/* (non-Javadoc)
	 * @see org.gpp.proj1.ui.controller.ISRGUIController#isServer()
	 */
	public boolean isServer() {
		
		return model.isServer();
	}

	/* (non-Javadoc)
	 * @see org.gpp.proj1.ui.controller.ISRGUIController#notifyServerName(java.lang.String)
	 */
	public void notifyServerName(String serverName) {

		for( ISRGUIView view : views ) {
			view.notifyServerName( serverName );
		}
	}

	/* (non-Javadoc)
	 * @see org.gpp.proj1.ui.controller.ISRGUIController#toggleReadinessState()
	 */
	public void toggleReadinessState() throws CommunicationException {
		
		model.toggleReadinessState();		
	}

	/* (non-Javadoc)
	 * @see org.gpp.proj1.ui.controller.ISRGUIController#mayStartGame()
	 */
	public boolean mayStartGame() {
		
		return model.mayStartGame();
	}

	/* (non-Javadoc)
	 * @see org.gpp.proj1.ui.controller.ISRGUIController#disconnect()
	 */
	public void disconnect() throws CommunicationException {
		
		model.disconnect();
	}

	/* (non-Javadoc)
	 * @see org.gpp.proj1.ui.controller.ISRGUIController#notifyDisconnected()
	 */
	public void notifyDisconnected() {
		
		for( ISRGUIView view : views ) {
			view.notifyDisconnected();
		}
	}

	/* (non-Javadoc)
	 * @see org.gpp.proj1.ui.controller.ISRGUIController#kickPlayer(java.lang.String)
	 */
	public void kickPlayer(String clientId) throws CommunicationException {
		
		this.model.kickPlayer( clientId );
	}

	/* (non-Javadoc)
	 * @see org.gpp.proj1.ui.controller.ISRGUIController#notifyClientDisconnected(java.lang.String)
	 */
	public void notifyClientDisconnected(String clientId) {
		
		for( ISRGUIView view : views ) {
			view.notifyClientDisconnected( clientId );
		}
	}

	/* (non-Javadoc)
	 * @see org.gpp.proj1.ui.controller.ISRGUIController#initGame()
	 */
	public void initGame() throws CommunicationException, ServerPhaseException {

		this.model.initGame();
	}

	/* (non-Javadoc)
	 * @see org.gpp.proj1.ui.controller.ISRGUIController#notifyInitPhaseFailed(java.lang.String)
	 */
	public void notifyInitPhaseFailed(String cause) {
	
		for( ISRGUIView view : views ) {
			view.notifyInitPhaseFailed( cause );
		}
	}

	/* (non-Javadoc)
	 * @see org.gpp.proj1.ui.controller.ISRGUIController#notifyInitPhaseStarted()
	 */
	public void notifyInitPhaseStarted() {

		for( ISRGUIView view : views ) {
			view.notifyInitPhaseStarted();
		}
	}

	/* (non-Javadoc)
	 * @see org.gpp.proj1.ui.controller.ISRGUIController#updateInitStatus(java.lang.String)
	 */
	public void updateInitStatus(String message) {

		for( ISRGUIView view : views ) {
			view.updateInitStatus( message );
		}
	}

	/**
	 * @param map
	 * @param positions
	 * @param robots
	 */
	public void gameHasStarted(GameMap map, Map<String, Map<String, RobotPosition>> positions, Map<String, Map<String, Map<RobotPartCategory, String>>> robots) {
		
		for( ISRGUIView view : views ) {
			view.gameHasStarted(  map, positions, robots );
		}
	}

	/* (non-Javadoc)
	 * @see org.gpp.proj1.ui.controller.ISRGUIController#notifyReadyToStartGame()
	 */
	public void notifyReadyToStartGame() throws CommunicationException {

		this.model.notifyReadyToStartGame();
	}

	/* (non-Javadoc)
	 * @see org.gpp.proj1.ui.controller.ISRGUIController#notifyCurrentTurn(java.lang.String, java.lang.String)
	 */
	public void notifyCurrentTurn(String clientId, String playerAlias) {
		
		this.waitForUiAccept();
		for( ISRGUIView view : views ) {
			view.notifyCurrentTurn( clientId, playerAlias );
		}
	}

	/* (non-Javadoc)
	 * @see org.gpp.proj1.ui.controller.ISRGUIController#notifyYourTurn()
	 */
	public void notifyYourTurn() {
		
		this.waitForUiAccept();
		for( ISRGUIView view : views ) {
			view.notifyYourTurn();
		}
	}

	/* (non-Javadoc)
	 * @see org.gpp.proj1.ui.controller.ISRGUIController#notifyTurnRobot(java.lang.String)
	 */
	public void notifyTurnRobot(String robotName) {
	
		this.waitForUiAccept();		
		for( ISRGUIView view : views ) {
			view.notifyTurnRobot( robotName );
		}
	}

	/* (non-Javadoc)
	 * @see org.gpp.proj1.ui.controller.ISRGUIController#getCurrentPlayerId()
	 */
	public String getCurrentPlayerId() throws CommunicationException {

		return this.model.getCurrentPlayerId();
	}

	/* (non-Javadoc)
	 * @see org.gpp.proj1.ui.controller.ISRGUIController#endTurn()
	 */
	public void endTurn() throws CommunicationException {
		
		this.model.endTurn();
	}

	/* (non-Javadoc)
	 * @see org.gpp.proj1.ui.controller.ISRGUIController#movedRobot(java.lang.String, int, int)
	 */
	public void movedRobot(String robot, int newX, int newY) throws CommunicationException {

		this.model.movedRobot( robot, newX, newY );		
	}

	/* (non-Javadoc)
	 * @see org.gpp.proj1.ui.controller.ISRGUIController#uiAcceptingMessages()
	 */
	public void uiAcceptingMessages() {
		
		synchronized ( uiLock ) {
			uiLock.notify();	
		}
	}

	private void waitForUiAccept() {
		synchronized ( uiLock ) {
			try {
				uiLock.wait();
			} catch (InterruptedException e) {
				LoggingSystem.getLogger().log( Level.SEVERE, "Could not notify UI" );
				return;
			}
		}
	}

	/* (non-Javadoc)
	 * @see org.gpp.proj1.ui.controller.ISRGUIController#notifyRobotMoved(java.lang.String, java.lang.String, int, int)
	 */
	public void notifyRobotMoved(String player, String robot, int x, int y) {

		this.waitForUiAccept();		
		for( ISRGUIView view : views ) {
			view.notifyRobotMoved( player, robot, x, y );
		}
	}

	/* (non-Javadoc)
	 * @see org.gpp.proj1.ui.controller.ISRGUIController#robotAttacked(java.lang.String, int, int, java.lang.String, java.lang.String)
	 */
	public void robotAttacked(String robot, int selectedX, int selectedY, String selectedPart, String selectedAttack) throws CommunicationException {

		this.model.robotAttacked(robot, selectedX, selectedY, selectedPart, selectedAttack);
	}

	public void notifyRobotAttacked(String player, String robot, int target, int target2, String part, String attack) {

		this.waitForUiAccept();		
		for( ISRGUIView view : views ) {
			view.notifyRobotAttacked(player, robot, target, target2, part, attack);
		}
	}
}
